package com.zuokun.dispatcher;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.zuokun.annotation.ProtocolMapping;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayOutputStream;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class ProtocolDispatcher {

    // 协议名 -> 方法信息
    private static final Map<String, Method> msgMethodMap = new HashMap<>();
    private static final Map<String, Object> instanceMap = new HashMap<>();

    private static final Map<String, Object> protoClassMap = new HashMap<>();

    private static final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * @param handlerInstance
     */
    public static void registerControllers(Object handlerInstance) {
        Class<?> clazz = handlerInstance.getClass();
        for (Method method : clazz.getDeclaredMethods()) {
            if (method.isAnnotationPresent(ProtocolMapping.class)) {
                ProtocolMapping annotation = method.getAnnotation(ProtocolMapping.class);
                String protoName = annotation.value();
                msgMethodMap.put(protoName, method);
                instanceMap.put(protoName, handlerInstance);
            }
        }
    }


    /**
     * 根据协议名调用处理方法
     *
     * @param protoName
     * @param body
     */
    public static byte[] dispatch(String protoName, Object body) {
        Method method = msgMethodMap.get(protoName);
        Object instance = instanceMap.get(protoName);
        if (method != null && instance != null) {
            try {
                //这里的instance就是controller类，body是参数
                Object result = method.invoke(instance, body);
                return transferToBytes(result.getClass().getSimpleName(), result);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            log.error("未找到协议：{} 的处理方法！", protoName);
        }
        return null;
    }

    /**
     * 将协议信息转为字节数组
     *
     * @param protoName
     * @param body
     * @return
     */
    public static byte[] transferToBytes(String protoName, Object body) {
        try {
            // 将协议名转换为 UTF-8 编码字节
            byte[] protoNameBytes = protoName.getBytes(StandardCharsets.UTF_8);
            int protoNameLength = protoNameBytes.length;

            // 假设你有一个 bodyToBytes() 方法将 body 对象序列化成字节数组
            byte[] bodyBytes = objectMapper.writeValueAsBytes(body);
            int bodyLength = bodyBytes.length;

            // 总长度 = 协议名长度字段(2字节) + 协议名内容 + 消息体
            int contentLength = 2 + protoNameLength + bodyLength;

            // 最终包长度 = contentLength + 消息头2字节
            ByteArrayOutputStream baos = new ByteArrayOutputStream();

            // 写入总长度（2字节，小端）
            baos.write(contentLength & 0xFF);
            baos.write((contentLength >> 8) & 0xFF);

            // 写入协议名长度（2字节）
            baos.write(protoNameLength & 0xFF);
            baos.write((protoNameLength >> 8) & 0xFF);

            // 写入协议名内容
            baos.write(protoNameBytes);

            // 写入消息体
            baos.write(bodyBytes);

            return baos.toByteArray();

        } catch (Exception e) {
            throw new RuntimeException("协议内容转换失败", e);
        }
    }


    /**
     * 注册协议类
     *
     * @param protocols
     */
    public static void registerProtocols(Map<String, Object> protocols) {
        for (String protocolName : protocols.keySet()) {
            protoClassMap.put(upperFirstChar(protocolName), protocols.get(protocolName));
        }
    }

    /**
     * 根据协议名获取协议类
     *
     * @param protocolName
     * @return
     */
    public static Object getProtocol(String protocolName) {
        return protoClassMap.get(protocolName);
    }

    /**
     * 首字母大写
     *
     * @param str
     * @return
     */
    public static String upperFirstChar(String str) {
        if (str == null || str.isEmpty()) {
            return str;
        }
        return Character.toUpperCase(str.charAt(0)) + str.substring(1);
    }
}